Android系统开发 Android10系统设置默认Launcher

您所在的位置:网站首页 adb设置默认launcher 安卓电视 Android系统开发 Android10系统设置默认Launcher

Android系统开发 Android10系统设置默认Launcher

2024-07-15 18:07| 来源: 网络整理| 查看: 265

版本声明

本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/17867429.html

本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 前言

  此博客讲解如何在Android10系统上,将自己的应用设置成默认Launcher。

 

第一步添加需要设置成Launcher的应用 首先在需要成为Launcher的清单文件里添加如下关键

注意,要添加singleTask,否则会出现home键多次创建launchar 应用

将应用导入系统中

路径::~/aosp/packages/apps/   如下图

Android.mk 内容如下

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) #### LOCAL_MODULE := Calligraphy LOCAL_MODULE_CLASS := APPS LOCAL_MODULE_TAGS := optional LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) LOCAL_PROPRIETARY_MODULE := true LOCAL_CERTIFICATE := PRESIGNED LOCAL_SRC_FILES := $(LOCAL_MODULE).apk include $(BUILD_PREBUILT) 到这一步,我们可以先选择编译系统,然后看看效果

这里可以看到这边启动了一个弹窗让你选择launcher。到这一步,我们就已经达到了一半的目的了

我们可以通过adb命令查看下这个弹窗,可以发现这个弹窗是叫一个ResolverActivity

第二步设置成默认Launcher

上面我们已经获知launcher的选择弹窗是ResolverActivity,现在目标很明确,就是修改ResolverActivity这个类,让它自动就选择我们需要的launcher应用,然后快速finish掉自己,不在显示。

ResolverActivity的路径:/aosp/frameworks/base/core/java/com/android/internal/app/ResolverActivity.java

在ResolverActivity添加如下代码(记得将中文注释删除,怕编译的时候不支持中文注释,这里只是说明一下简单的理解):

//add:Setdefaultluncher private void setDefaultLauncher(String defPackageName, String defClassName) { try { final PackageManager pm = getPackageManager(); Log.i("deflauncherxxz", "deflauncher : PackageName = " + defPackageName + " ClassName = " + defClassName); //这里组装一个我们需要启动的launcher的IntentFilter,以提供PackageManager直接启动 IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.MAIN"); filter.addCategory("android.intent.category.HOME"); filter.addCategory("android.intent.category.DEFAULT"); //这里创建一个intent并且添加Intent.ACTION_MAIN与Intent.CATEGORY_HOME,调用PackageManager的queryIntentActivities搜索符合的ResolveInfo列表数据 //简单的来说就是希望搜索下系统中有CATEGORY_HOME(设备启动显示的第一个活动)的数据 Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); List list = new ArrayList(); //queryIntentActivities可以检索针对给定意图可以执行的所有活动。 list = pm.queryIntentActivities(intent, 0); final int num = list.size(); ComponentName[] set = new ComponentName[num]; int bestMatch = 0; for (int i = 0; i < num; i++) { ResolveInfo r = list.get(i); set[i] = new ComponentName(r.activityInfo.packageName, r.activityInfo.name); //match是一个整数,它表示了应用程序与目标IntentFilter的匹配程度。这个值是由系统根据ResolveInfo对象中的各种信息(如包名、类名、动作、类别等)与目标IntentFilter的匹配程度来计算的。 if (r.match > bestMatch) bestMatch = r.match; } //选择首选活动 ComponentName preferredActivity = new ComponentName(defPackageName, defClassName); //将组合好的数据添加到首选活动中 pm.addPreferredActivity(filter, bestMatch, set, preferredActivity); } catch (Exception e) { e.printStackTrace(); } }

然后在onCreate方法里以下图方式调用setDefaultLauncher

然后编译,在make之前记得执行一下 make update-api, 因为我们更新了ResolverActivity类中的api

分析一波

这里分析一波,ResolverActivity是如何执行的并且上面的setDefaultLauncher方法其实有参考来源的。  只要明白了这部分,以后的Android其他版本我们也能大致明白如何修改与实现。

首先ResolverActivity方法是通过下面这个configureContentView方法配置需要显示的Launcher选择对话框内容的。

/** * Returns true if the activity is finishing and creation should halt */ public boolean configureContentView(List payloadIntents, Intent[] initialIntents, List rList) { // The last argument of createAdapter is whether to do special handling // of the last used choice to highlight it in the list. We need to always // turn this off when running under voice interaction, since it results in // a more complicated UI that the current voice interaction flow is not able // to handle. mAdapter = createAdapter(this, payloadIntents, initialIntents, rList, mLaunchedFromUid, mSupportsAlwaysUseOption && !isVoiceInteraction()); boolean rebuildCompleted = mAdapter.rebuildList(); if (useLayoutWithDefault()) { mLayoutId = R.layout.resolver_list_with_default; } else { mLayoutId = getLayoutResource(); } setContentView(mLayoutId); int count = mAdapter.getUnfilteredCount(); // We only rebuild asynchronously when we have multiple elements to sort. In the case where // we're already done, we can check if we should auto-launch immediately. if (rebuildCompleted) { if (count == 1 && mAdapter.getOtherProfile() == null) { // Only one target, so we're a candidate to auto-launch! final TargetInfo target = mAdapter.targetInfoForPosition(0, false); if (shouldAutoLaunchSingleChoice(target)) { safelyStartActivity(target); mPackageMonitor.unregister(); mRegistered = false; finish(); return true; } } } mAdapterView = findViewById(R.id.resolver_list); if (count == 0 && mAdapter.mPlaceholderCount == 0) { final TextView emptyView = findViewById(R.id.empty); emptyView.setVisibility(View.VISIBLE); mAdapterView.setVisibility(View.GONE); } else { mAdapterView.setVisibility(View.VISIBLE); onPrepareAdapterView(mAdapterView, mAdapter); } return false; }

在上面的代码中onPrepareAdapterView组织适配器方法下一步的关键

看看ItemClickListener点击监听

看看startSelected方法

在下面的onTargetSelected方法中,就可以找到我们自己实现的setDefaultLauncher参考来源部分代码的。

protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { final ResolveInfo ri = target.getResolveInfo(); final Intent intent = target != null ? target.getResolvedIntent() : null; if (intent != null && (mSupportsAlwaysUseOption || mAdapter.hasFilteredItem()) && mAdapter.mUnfilteredResolveList != null) { // Build a reasonable intent filter, based on what matched. IntentFilter filter = new IntentFilter(); Intent filterIntent; if (intent.getSelector() != null) { filterIntent = intent.getSelector(); } else { filterIntent = intent; } String action = filterIntent.getAction(); if (action != null) { filter.addAction(action); } Set categories = filterIntent.getCategories(); if (categories != null) { for (String cat : categories) { filter.addCategory(cat); } } filter.addCategory(Intent.CATEGORY_DEFAULT); int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK; Uri data = filterIntent.getData(); if (cat == IntentFilter.MATCH_CATEGORY_TYPE) { String mimeType = filterIntent.resolveType(this); if (mimeType != null) { try { filter.addDataType(mimeType); } catch (IntentFilter.MalformedMimeTypeException e) { Log.w("ResolverActivity", e); filter = null; } } } if (data != null && data.getScheme() != null) { // We need the data specification if there was no type, // OR if the scheme is not one of our magical "file:" // or "content:" schemes (see IntentFilter for the reason). if (cat != IntentFilter.MATCH_CATEGORY_TYPE || (!"file".equals(data.getScheme()) && !"content".equals(data.getScheme()))) { filter.addDataScheme(data.getScheme()); // Look through the resolved filter to determine which part // of it matched the original Intent. Iterator pIt = ri.filter.schemeSpecificPartsIterator(); if (pIt != null) { String ssp = data.getSchemeSpecificPart(); while (ssp != null && pIt.hasNext()) { PatternMatcher p = pIt.next(); if (p.match(ssp)) { filter.addDataSchemeSpecificPart(p.getPath(), p.getType()); break; } } } Iterator aIt = ri.filter.authoritiesIterator(); if (aIt != null) { while (aIt.hasNext()) { IntentFilter.AuthorityEntry a = aIt.next(); if (a.match(data) >= 0) { int port = a.getPort(); filter.addDataAuthority(a.getHost(), port >= 0 ? Integer.toString(port) : null); break; } } } pIt = ri.filter.pathsIterator(); if (pIt != null) { String path = data.getPath(); while (path != null && pIt.hasNext()) { PatternMatcher p = pIt.next(); if (p.match(path)) { filter.addDataPath(p.getPath(), p.getType()); break; } } } } } if (filter != null) { final int N = mAdapter.mUnfilteredResolveList.size(); ComponentName[] set; // If we don't add back in the component for forwarding the intent to a managed // profile, the preferred activity may not be updated correctly (as the set of // components we tell it we knew about will have changed). final boolean needToAddBackProfileForwardingComponent = mAdapter.mOtherProfile != null; if (!needToAddBackProfileForwardingComponent) { set = new ComponentName[N]; } else { set = new ComponentName[N + 1]; } int bestMatch = 0; for (int i=0; i bestMatch) bestMatch = r.match; } if (needToAddBackProfileForwardingComponent) { set[N] = mAdapter.mOtherProfile.getResolvedComponentName(); final int otherProfileMatch = mAdapter.mOtherProfile.getResolveInfo().match; if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch; } if (alwaysCheck) { final int userId = getUserId(); final PackageManager pm = getPackageManager(); // Set the preferred Activity pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent()); if (ri.handleAllWebDataURI) { // Set default Browser if needed final String packageName = pm.getDefaultBrowserPackageNameAsUser(userId); if (TextUtils.isEmpty(packageName)) { pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName, userId); } } } else { try { mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch); } catch (RemoteException re) { Log.d(TAG, "Error calling setLastChosenActivity\n" + re); } } } } if (target != null) { safelyStartActivity(target); // Rely on the ActivityManager to pop up a dialog regarding app suspension // and return false if (target.isSuspended()) { return false; } } return true; }

上面的关键点是addPreferredActivity,然后可以看看 filter / bestMatch / set / intent.getComponent()  这几个参数的来源

end



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3